package net.apps.core.meta;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class DeclMetaImpl implements Meta {
	
	private final Meta parent_meta;
	private final int id;
	private final String name;
	private final String description;
	private final Collection<Meta> decls; 
	private final Meta[] typedecls;
	private final Meta[] fielddecls;
	
	DeclMetaImpl(Meta parent_meta, Class<?> clazz) {
		this.parent_meta = parent_meta;
		AMessage ann = clazz.getAnnotation(AMessage.class);
		this.id = ann.id();
		String name = ann.name();
		if (name == null || name.length() == 0)
			name = clazz.getSimpleName();
		this.name = name;
		this.description = ann.description();
		
		ArrayList<Meta> decls = new ArrayList<Meta>();
		
		{
			ArrayList<Meta> types = new ArrayList<Meta>();
			for (Class<?> msgclz : clazz.getDeclaredClasses()) {
				if (msgclz.isAnnotationPresent(AMessage.class)) {
					DeclMetaImpl dm = new DeclMetaImpl(this, msgclz);
					types.add(dm);
					decls.add(dm);
				}
			}
			if (types.size() > 0) {
				int max_id = 0;
				for (Meta dm : types)
					max_id = Math.max(max_id, dm.getId());
				this.typedecls = new Meta[max_id + 1];
				for (Meta dm : types)
					this.typedecls[dm.getId()] = dm;
			} else {
				this.typedecls = new Meta[0];
			}
		}
		
		{
			ArrayList<Meta> fields = new ArrayList<Meta>();
			for (Field fld : clazz.getDeclaredFields()) {
				if (fld.isAnnotationPresent(AField.class)) {
					FieldMetaImpl fm = new FieldMetaImpl(this, fld);
					fields.add(fm);
					decls.add(fm);
				}
			}
			if (fields.size() > 0) {
				int max_id = 0;
				for (Meta dm : fields)
					max_id = Math.max(max_id, dm.getId());
				this.fielddecls = new Meta[max_id + 1];
				for (Meta dm : fields)
					this.fielddecls[dm.getId()] = dm;
			} else {
				this.fielddecls = new Meta[0];
			}
		}
		
		this.decls = Collections.unmodifiableList(decls);
	}

	@Override
	public Decl getDeclKind() {
		return Decl.PROTOCOL;
	}

	@Override
	public int getId() {
		return id;
	}

	@Override
	public Meta getType() {
		return MetaFactory.metaOfDecl;
	}

	@Override
	public Collection<Meta> getConstraints() {
		return Collections.emptyList();
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public String getDescription() {
		return description;
	}

	@Override
	public Meta getParent() {
		return parent_meta;
	}
	
	@Override
	public Collection<Meta> getDecls() {
		return decls;
	}

	@Override
	public Meta getTypeDecl(int id) {
		return typedecls[id];
	}

	@Override
	public Object getValue() {
		return null;
	}

}
